package xyz.renyumeng.xkm.order.service.impl;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.*;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import xyz.renyumeng.xkm.client.es.EsShopFeignClient;
import xyz.renyumeng.xkm.client.shop.ProductInfoFeignClient;
import xyz.renyumeng.xkm.client.user.UserInfoFeignClient;
import xyz.renyumeng.xkm.dto.order.AdminOrderPage;
import xyz.renyumeng.xkm.dto.order.AdminOrderPageResult;
import xyz.renyumeng.xkm.exception.XkmException;
import xyz.renyumeng.xkm.model.order.OrderInfo;
import xyz.renyumeng.xkm.model.shop.ProductInfo;
import xyz.renyumeng.xkm.model.user.UserInfo;
import xyz.renyumeng.xkm.order.mapper.OrderMapper;
import xyz.renyumeng.xkm.order.service.OrderService;
import xyz.renyumeng.xkm.result.Result;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

import static xyz.renyumeng.xkm.rabbitMQ.RabbitMqConstant.*;
import static xyz.renyumeng.xkm.rabbitMQ.RabbitMqConstant.DELAY_PRODUCT_ROUTING_KEY;
import static xyz.renyumeng.xkm.redis.RedisConstants.*;

/**
 * @author renyumeng
 * @date Created in 8:51 2023/6/30
 */

@Service
@Slf4j
@Transactional
public class OrderServiceImpl extends ServiceImpl<OrderMapper, OrderInfo> implements OrderService {

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private UserInfoFeignClient userInfoFeignClient;

    @Resource
    private ProductInfoFeignClient productInfoFeignClient;

    @Resource
    private EsShopFeignClient esShopFeignClient;

    @Resource
    private RabbitTemplate rabbitTemplate;

    @Override
    public Result<List<Long>> addOrders(List<OrderInfo> orderInfoList) {
        List<Long> orderIdList = new ArrayList<>();
        orderInfoList.forEach(orderInfo -> {

            this.addOrder(orderInfo, orderIdList);
        });
        return Result.ok(orderIdList).message("创建订单成功!");
    }

    @Override
    public Boolean innerAddOrder(OrderInfo orderInfo) {
        return this.save(orderInfo);
    }

    @Override
    public Result<AdminOrderPageResult> pageWithFilter(AdminOrderPage adminOrderPage) {
        Long page = adminOrderPage.getPage();
        Long count = adminOrderPage.getCount();
        Page<OrderInfo> orderInfoPage = new Page<>(page, count);
        LambdaQueryWrapper<OrderInfo> orderInfoLambdaQueryWrapper = new LambdaQueryWrapper<>();
        orderInfoLambdaQueryWrapper.le(!(StringUtils.isEmpty(adminOrderPage.getEndTime())), OrderInfo::getCreateTime, adminOrderPage.getEndTime());
        orderInfoLambdaQueryWrapper.ge(!(StringUtils.isEmpty(adminOrderPage.getStartTime())), OrderInfo::getCreateTime, adminOrderPage.getStartTime());
        Page<OrderInfo> resultPage = this.page(orderInfoPage);
        List<OrderInfo> orderInfoList = resultPage.getRecords();
        orderInfoList.forEach(this::getDescOrderInfoById);

        AdminOrderPageResult adminOrderPageResult = new AdminOrderPageResult();
        adminOrderPageResult.setData(orderInfoList);
        adminOrderPageResult.setSize(resultPage.getSize());
        adminOrderPageResult.setCurrent(resultPage.getCurrent());
        adminOrderPageResult.setTotal(resultPage.getTotal());
        adminOrderPageResult.setHasNext(resultPage.hasNext());
        adminOrderPageResult.setHasPrevious(resultPage.hasPrevious());
        return Result.ok(adminOrderPageResult).message("查询成功!");
    }

    private void getDescOrderInfoById(OrderInfo orderInfo) {
        Long ownerId = orderInfo.getOwnerId();
        Long productId = orderInfo.getProductId();
        Long purchaseUserId = orderInfo.getPurchaseUserId();

        UserInfo ownerUserInfo = userInfoFeignClient.innerGetUserByIdWithAddressInfo(ownerId);
        if (ObjectUtil.isEmpty(ownerUserInfo)) {
            throw new XkmException(201, "发布用户信息不存在!");
        }
        UserInfo purchaseUserInfo = userInfoFeignClient.innerGetUserByIdWithAddressInfo(purchaseUserId);
        if (ObjectUtil.isEmpty(purchaseUserInfo)) {
            throw new XkmException(201, "购买用户信息不存在!");
        }
        ProductInfo productInfo = productInfoFeignClient.innerGetProductInfoAndOwnerUserInfoById(productId);
        if (ObjectUtil.isEmpty(productInfo)) {
            throw new XkmException(201, "商品信息不存在!");
        }
        orderInfo.getParam().put("ownerUsername", ownerUserInfo.getUsername());
        orderInfo.getParam().put("purchaseUsername", purchaseUserInfo.getUsername());
        orderInfo.getParam().put("productName", productInfo.getProductName());
        orderInfo.getParam().put("productDesc", productInfo.getDesc());
        orderInfo.getParam().put("productType", productInfo.getParam().get("type"));
        Object addressInfoObject = purchaseUserInfo.getParam().get("addressInfo");
        String addressInfo = Convert.toStr(addressInfoObject);
        orderInfo.setAddress(addressInfo);
    }

    private void addOrder(OrderInfo orderInfo, List<Long> orderIdList) {
        String lockKey = LOCK_SHOP_KEY + orderInfo.getProductId();
        boolean isLock = this.tryLock(lockKey);
        // 加锁如果获取锁失败就直接返回
        if (BooleanUtil.isFalse(isLock)) {
            orderIdList.add(null);
            return;
        }

        // 查es里面的订单信息如果isList=1返回
        if (BooleanUtil.isTrue(esShopFeignClient.getEsProductIdWithIsListed(orderInfo.getProductId(), 0))) {
            orderIdList.add(null);
            return;
        }
        stringRedisTemplate.opsForValue().set(lockKey, "1", LOCK_SHOP_TTL, TimeUnit.MINUTES);
        // 生成订单id
        int workId = RandomUtil.randomInt(0, 31);
        String orderId = IdUtil.getSnowflake(workId, 1).nextIdStr();
        String queueOrderKey = QUEUE_ORDER_KEY + orderInfo.getPurchaseUserId().toString() + ":" + orderId;
        orderIdList.add(Long.parseLong(orderId));
        orderInfo.setId(Convert.toLong(orderId));
        // TODO 在创建订单的时候锁定商品isListed=1发送消息到shopService将isList修改为1
        rabbitTemplate.convertAndSend(PRODUCT_EXCHANGE, PRODUCT_DE_LIST_ROUTING_KEY, orderInfo);

        // TODO 发送ttl为30min的消息到延时队列(死信交换机) 30min后通知orderService数据库是否存在该订单
        rabbitTemplate.convertAndSend(DELAY_PRODUCT_EXCHANGE, DELAY_PRODUCT_ROUTING_KEY, orderInfo);
        // 写redis
        stringRedisTemplate
                .opsForValue()
                .setIfAbsent(queueOrderKey, JSONUtil.toJsonStr(orderInfo), QUEUE_ORDER_TTL, TimeUnit.HOURS);
    }


    public boolean tryLock(String key) {
        Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(key, "1", LOCK_SHOP_TTL, TimeUnit.SECONDS);
        return BooleanUtil.isTrue(flag);
    }

}
